goog.require('dkpoints');
goog.require('dkpoints.App');
goog.require('dkpoints.ServiceProvider');

goog.require('goog.testing.jsunit');


var app = null,
    mockAuth, mockMenu, mockNav, mockRPC, mockStatus,
    realAuth, realMenu, realNav, realRPC, realStatus, realServiceProvider;


var setUpPage = function() {
  realNav = dkpoints.nav.Nav;
  realAuth = dkpoints.auth.Auth;
  realRPC = dkpoints.rpc.RPCService;
  realMenu = dkpoints.menu.Menu;
  realStatus = dkpoints.status.Status;
  realServiceProvider = dkpoints.ServiceProvider;
};


var setUp = function() {
  mockAuth = dkpoints.auth.Auth = function() {'Auth'};
  mockAuth.prototype.init = function() {};

  mockMenu = dkpoints.menu.Menu = function() {'Menu'};
  mockNav = dkpoints.nav.Nav = function() {'Nav'};
  mockRPC = dkpoints.rpc.RPCService = function() {'RPC'};
  mockStatus = dkpoints.status.Status = function() {'Status'};

  dkpoints.ServiceProvider = realServiceProvider;
};


/**
 * Make sure that the dkpoints object exists.
 */
var testDKPointsExists = function() {
  assertNotNullNorUndefined('should be defined and not null', dkpoints);
};


/**
 * Make sure that dkpoints.getApp() returns an instance of the app after
 * one has been constructed.
 */
var testGetAppReturnsApp = function() {
  app = new dkpoints.App();
  assertTrue('dkpoints.getApp() should return a dkpoints.App',
             dkpoints.getApp() instanceof dkpoints.App);
};


/**
 * Test that the constructor throws the appropriate errors when the required
 * elements are missing from the page (#body_content and #menu_content);
 */
var testAppConstructorThrowsIFRequiredDOMElementsAreMissing = function() {
  var body = document.body,
      bodyContent = document.getElementById('body_content');
  bodyContent.parentNode.removeChild(bodyContent);

  // Have to wrap the constructor in a function because assertThrows won't use
  // new.
  var run = function() {
    new dkpoints.App();
  };

  assertThrows('The constructor should throw an error if body_content is ' +
               'not in the page.', run);

  body.insertBefore(bodyContent, body.firstChild);

  var menuContent = document.getElementById('menu_content');
  menuContent.parentNode.removeChild(menuContent);

  assertThrows('The constructor should throw an error if body_content is ' +
               'not in the page.', run);

  body.insertBefore(menuContent, bodyContent.nextSibling);
};


/**
 * Test that the dkpoints.App constructor passes the appropriate dependencies
 * into its subsystem constructors.
 */
var testAppConstructorGivesSubsystemsProperDependencies = function() {
  var authDepsCorrect = false;
  mockAuth = dkpoints.auth.Auth = function(testRPC, testMenu, testStatus) {
    if ((testRPC instanceof mockRPC) &&
        (testMenu instanceof mockMenu) &&
        (testStatus instanceof mockStatus)) {
      authDepsCorrect = true;
    }
  };
  mockAuth.prototype.init = function() {};

  var menuDepsCorrect = false;
  mockMenu = dkpoints.menu.Menu = function(menuContent) {
    if (menuContent === document.getElementById('menu_content')) {
      menuDepsCorrect = true;
    }
  };

  // This test implicitly serves as a test that dkpoints.App is passing the
  // correct parameters to the ServiceProvider constructor.
  var navDepsCorrect = false;
  mockNav = dkpoints.nav.Nav = function(serviceProvider) {
    var sp = serviceProvider;
    if (sp instanceof dkpoints.ServiceProvider &&
        sp.getAuth() instanceof mockAuth &&
        sp.getRPC() instanceof mockRPC &&
        sp.getStatusManager() instanceof mockStatus &&
        sp.getBodyContent() === document.getElementById('body_content')) {
      navDepsCorrect = true;
    }
  };

  var statusDepsCorrect = false;
  mockStatus = dkpoints.status.Status = function(testBody, testBodyContent) {
    if (testBody === document.body &&
        testBodyContent === document.getElementById('body_content') &&
        testBodyContent !== null) {
      statusDepsCorrect = true;
    }
  };

  app = new dkpoints.App();

  assertTrue('Deps passed to the Auth constructor were not correct.',
             authDepsCorrect);
  assertTrue('Deps passed to the Menu constructor were not correct.',
             menuDepsCorrect);
  assertTrue('Deps passed to the Nav constructor were not correct.',
             navDepsCorrect);
  assertTrue('Deps passed to the Status constructor were not correct.',
             statusDepsCorrect);
};


/**
 * Test that ServiceProvider properly stores and returns its fields.
 */
var testServiceProviderStoresAndReturnsFields = function() {
  var auth = new mockAuth,
      rpc = new mockRPC,
      status = new mockStatus,
      bodyContent = document.getElementById('body_content'),
      sp = new dkpoints.ServiceProvider(auth, rpc, status, bodyContent);

  assertEquals('getAuth should return the given auth instance.',
               auth, sp.getAuth());
  assertEquals('getRPC should return the given rpc instance.',
               rpc, sp.getRPC());
  assertEquals('getStatusManager should return the given status instance.',
               status, sp.getStatusManager());
  assertEquals('getBodyContent should return the given bodyContent reference.',
               bodyContent, sp.getBodyContent());
};


var tearDown = function() {
  app = null;
};


// Reset the stubs.
var tearDownPage = function() {
  dkpoints.nav.Nav = realNav;
  dkpoints.auth.Auth = realAuth;
  dkpoints.rpc.RPCService = realRPC;
  dkpoints.menu.Menu = realMenu;
  dkpoints.status.Status = realStatus;
};
